//////////////////////////////////////////////////////////////////
// MattiseOS Kernel
// Date: 2008
// Author: Matthew Iselin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////

// Includes
#include <mattise.h>
#include <stdint.h>
#include <core/pit.h>
#include <kernio/io.h>
#include <process/common.h>

// Functions

// sleeps for a certain number of seconds
uint32_t sleep( uint32_t seconds )
{
	// don't write the same function twice!
	return delay(seconds * 1000);
}

// sleeps for a certain number of milliseconds
uint32_t delay( uint32_t t )
{
	// get the cpu flags
	uint32_t int_state = 0;
	asm volatile( "pushf; popl %0" : "=r" (int_state) );

	// clear the interrupt flag - we're messing around with process states here
	asm volatile( "cli" );

	// setup the sleeper
	currprocess->sleep_start_time = kGetPITCounter();
	currprocess->sleep_end_time = kGetPITCounter() + (t);
	currprocess->sleep_curr_time = 0;

#if DEBUG
	dprintf( "[sleep] process delay: %d, %d, %d\n", currprocess->sleep_start_time, currprocess->sleep_end_time, currprocess->sleep_curr_time );
#endif

	// take it off the ready state
	currprocess->status = PSTATE_SLEEPING;

	// get a pointer to this process
	struct pentry* ent = currprocess->ent;

	// unlink from the current queue
	if(ent->prev)
		ent->prev->next = ent->next;
	else
	{
		// if the previous is null then this is actually the first item
		// on the already queue, so we need to fix that
		if(ent == p_alreadyq)
			p_alreadyq = ent->next;
		else if(ent == p_readyq)
			p_readyq = ent->next;
	}
	if(ent->next)
		ent->next->prev = ent->prev;

	// link onto the sleep queue
	if(p_sleepq == 0)
	{
		p_sleepq = ent;
		ent->prev = ent->next = 0;
	}
	else
	{
		p_sleepq->prev = ent;
		ent->prev = 0;
		ent->next = p_sleepq;
		p_sleepq = ent;
	}

	// go to a ready context
	kSchedule(0);

	// restore interrupts, if needed
	if( int_state & 0x200 )
		asm volatile( "sti" );

	// return the time
	return t;
}
